package dy.com.tcp结合udp.Server;

import dy.com.tcp结合udp.constants.UDPConstants;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.nio.ByteBuffer;
import java.util.UUID;


public class UDPServerProvider {
    private static Provider PROVIDER_INSTANCE;

    static void start(int port) {
        //调用stop（）,停止还存活的监听线程
        stop();
        //新建一个监听线程
        String sn = UUID.randomUUID().toString();
        // System.out.println(sn);
        Provider provider = new Provider(sn, port);
        provider.start();
        PROVIDER_INSTANCE = provider;
    }

    static void stop() {
        //如果不为空，则停止
        if (PROVIDER_INSTANCE != null) {
            PROVIDER_INSTANCE.exit();
            PROVIDER_INSTANCE = null;
        }

    }

    private static class Provider extends Thread {
        byte[] sn;
        //服务器TCP的端口号
        int port;
        //udp socket
        DatagramSocket ds = null;
        //判断是否结束
        boolean finish = false;
        //存储接收到消息的buffer
        byte[] buffer = new byte[256];

        //构造函数，提供身份信息和要监听的端口
        Provider(String sn, int port) {
            this.port = port;
            this.sn = sn.getBytes();
        }

        @Override
        public void run() {
            System.out.println("服务器UDP端口开始监听！");
            try {
                //初始化 udp socket，并监听 server 的udp port端口
                ds = new DatagramSocket(UDPConstants.UDP_PORT_SERVER);
                //构建udp的 数据报,监听时无需指定ip
                DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
                //当没有结束监听时
                while (!finish) {
                    //接收
                    ds.receive(dp);

                    //收到的客户端的信息
                    String clientIp = dp.getAddress().getHostAddress();
                    int clientPort = dp.getPort();
                    byte[] clientData = dp.getData();
                    int count = dp.getLength();
                    //判断接收数据是否合理,长度大于口令长度+cmd长度（2）+port长度（4）,并且以口令为开头
                    boolean valid = count >= UDPConstants.HEADER.length + 2 + 4 &&
                            new String(clientData).startsWith(new String(UDPConstants.HEADER));

                    System.out.println("收到客户端的udp请求，来自ip：" + clientIp + ",端口：" + clientPort + ",合理：" + valid);
                    if (!valid) {
                        continue;
                    }
                    //解析data信息,借助bytebuffer，从header.length开始解析到count位置
                    ByteBuffer byteBuffer = ByteBuffer.wrap(clientData, UDPConstants.HEADER.length, count);
                    short cmd = byteBuffer.getShort();
                    int clientUDPPort = byteBuffer.getInt();

                    //收到合理信息
                    if (cmd == 1 || clientUDPPort > 0) {
                        System.out.println("UDPSearcher receive cmd:" + cmd + "serverPort:" + clientUDPPort);
                        //构建回传数据包
                        ByteBuffer responseBuffer = ByteBuffer.wrap(buffer);
                        responseBuffer.put(UDPConstants.HEADER);
                        responseBuffer.putShort((short) 2);
                        responseBuffer.putInt(port);
                        responseBuffer.put(sn);
                        int len = responseBuffer.position();
                        DatagramPacket responsePacket = new DatagramPacket(buffer, len, dp.getAddress(), clientUDPPort);
                        ds.send(responsePacket);
                        System.out.println("服务器UDP已经发送信息到ip：" + clientIp + ",端口:" + clientUDPPort);
                    } else {
                        System.out.println("服务器UDP接收到的cmd:" + cmd + ",port:" + clientPort);
                    }
                }

            } catch (IOException e) {
                //     e.printStackTrace();
            } finally {
                close();
            }
            System.out.println("服务器监听udp结束");
        }

        void close() {
            if (ds != null) {
                ds.close();
                ds = null;
            }
        }

        void exit() {

            finish = true;
            close();
        }
    }
}
